<?php

namespace Gql\Core;


use Gql\Contracts\MiddlewareInterface;
use Gql\Contracts\TypeAbstract;
use Gql\Middleware\InitialMiddleware;
use Gql\Middleware\ResolveBaseMiddleware;
use Gql\Middleware\ResolveMiddleware;
use Gql\ScalarType\ImgType;
use Gql\ScalarType\KeyType;
use Gql\ScalarType\MixedType;
use GraphQL\Executor\ExecutionResult;
use GraphQL\GraphQL;
use GraphQL\Utils\BuildSchema;
use Illuminate\Pipeline\Pipeline;

class Application
{
    public $debug = false;
    public $context;
    public $pipeline;
    public $schema;

    public $query = '';
    public $variables = [];
    public $rootValue = null;

    public $middleware = [
        InitialMiddleware::class,
        ResolveBaseMiddleware::class,
        ResolveMiddleware::class,
    ];

    public function __construct(Context $context, Pipeline $pipeline, Parse $parse)
    {
        $this->context = $context;
        $this->pipeline = $pipeline;
        $this->debug = config('gql.debug');
        $parse->setEntryFile(config('gql.entry_file'));
        $parse->setCacheFile(config('gql.cache_file'));
        $this->schema = BuildSchema::build($parse->getDocument($this->debug), function ($typeConfig, $typeDefinitionNode) {
            switch ($typeConfig['name']){
                case TypeAbstract::KEY:
                    $typeConfig = KeyType::create($typeConfig);
                    break;
                case TypeAbstract::IMG:
                    $typeConfig = ImgType::create($typeConfig);
                    break;
                case TypeAbstract::MIXED:
                    $typeConfig = MixedType::create($typeConfig);
                    break;
            }
            return $typeConfig;
        });


    }

    /**
     * @return \GraphQL\Type\Schema
     */
    public function getSchema()
    {
        return $this->schema;
    }

    /**
     * 执行查询
     * @return ExecutionResult
     */
    public function execute(): ExecutionResult
    {
        $rootValue = [
            //'Mixed'=>new MixedType(),
        ];
        return GraphQL::executeQuery(
            $this->schema,
            $this->query,
            $rootValue,
            null,
            $this->variables,
            null,
            $this->handle()
        );
    }

    /**
     * @param string $query
     * @return Application
     */
    public function setQuery(string $query): self
    {
        $this->query = $query;
        return $this;
    }

    /**
     * @param $rootValue
     * @return Application
     */
    public function setRootValue($rootValue): self
    {
        $this->rootValue = $rootValue;
        return $this;
    }

    /**
     * @param array|null $variables
     * @return Application
     */
    public function setVariables(?array $variables): self
    {
        if (is_array($variables)) {
            $this->variables = $variables;
        }
        return $this;
    }

    /**
     * @param $error
     * @return Application
     */
    public function setTokenError($error): self
    {
        $this->context->setTokenError($error);
        return $this;
    }


    /**
     * 新增处理中间件
     * @param $middle
     * @return Application
     */
    public function pushMiddleware($middle): self
    {
        if (is_array($middle)) {
            array_push($this->middleware, $middle);
        } elseif ($middle instanceof MiddlewareInterface || is_callable($middle)) {
            array_push($this->middleware, $middle);
        }
        return $this;
    }

    /**
     * 字段处理函数
     * @return callable
     */
    public function handle(): callable
    {
        $context = $this->context;
        $pipeline = $this->pipeline;
        $middleware = $this->middleware;
        return function (...$args) use ($context, $pipeline, $middleware) {
            $pipeline->send($context->init(...$args));
            $pipeline->through($middleware);
            return $pipeline->then(function ($res) {
                return $res;
            });
        };
    }
}